package org.boulderfoodrescue.webapp.shared;

import java.io.Serializable;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import org.boulderfoodrescue.webapp.client.pickuptimes.timepicker.PickupWindow;

import com.google.gwt.i18n.client.DateTimeFormat;


public class RecurringEvent implements Serializable, HasEventSummary{
	private static final long serialVersionUID = -8367524216396571409L;

	private Long databaseKey;
	private Long locationKey;

	/**
	 * The repetition type for this particular event.
	 * 
	 * @author Sean Wiese
	 *
	 */
	public enum RepeatType{Daily, Weekly, Monthly, Yearly}

	private RepeatType repeatType;

	/**
	 * The Frequency of the repetitions. i.e. a value of 
	 * 1 would mean every week if the repeat type was weekly
	 * or a value of 2 would mean every two weeks.
	 */
	private Long frequency;

	// Code for all Repeat Types

	private Boolean allDay;
	private Date startDate;
	private Date startTime;
	private Date endTime;

	/**
	 * The type of ending for the event. Never means does not end.
	 * AfterNumOccurences means it ends after a certain number of 
	 * event occurrences. On means that it ends on a particular date.
	 * 
	 * @author Sean Wiese
	 *
	 */
	public enum EndType{Never, AfterNumOccurrences, On};
	private EndType endType;

	/**
	 * The number of occurrences until the event ends.
	 */
	private Long numberOfOccurences;


	/**
	 * The last date of the event
	 */
	private Date endDate;

	/**
	 * List of OneTimeEvents that have been modified by the 
	 * system. 
	 */
	private List<OneTimeEvent> modifiedEventsInSeries;

	// Code for Weekly Repeat Type

	/**
	 * A string for representing which days the event repeats on.
	 * Each char in the string represents a day from Sunday to Saturday
	 * example = "1001011" would be an event that occurs on Sundays, Wednesdays,
	 * Fridays, and Saturdays.
	 */
	private String repeatsOn;


	// Code for Monthly Repeats

	/**
	 * If true then we repeat on start date of the
	 * event number day of the month otherwise we
	 * repeat on the day of week that corresponds to the
	 * start date.
	 * For example if true and the start date was April 11th 2012 we 
	 * would repeat on the 11th of every month. 
	 * If false we would repeat every month on the second
	 * Wednesday. 
	 */
	private Boolean dayOfMonth;



	public RecurringEvent(){
		repeatType = RepeatType.Daily;
		frequency = 1l;
		startDate = new Date();
		endType = EndType.Never;
		modifiedEventsInSeries = new LinkedList<OneTimeEvent>();
		repeatsOn = "0000000";
		allDay = false;
		dayOfMonth = true;
	}

	public RecurringEvent(PickupWindow pw) {
		this();
		switch(pw.getRepeatType()){
		case None:
		case Daily:
			this.repeatType = RepeatType.Daily;
			break;
		case Weekly:
			this.repeatType = RepeatType.Weekly;
			this.repeatsOn = pw.getRepeatsOn();
			break;
		case Monthly:
			this.repeatType = RepeatType.Monthly;
			this.dayOfMonth = (pw.getRepeatBy() == 0) ? true : false;
			break;
		case Yearly:
			this.repeatType = RepeatType.Yearly;
			break;
		}

		switch(pw.getEndType()){
		case 0: 
			this.endType = EndType.Never;
			break;
		case 1:
			this.endType = EndType.AfterNumOccurrences;
			this.numberOfOccurences = new Long(pw.getNumOccurances());
			break;
		case 2:
			this.endType = EndType.On;
			this.endDate = pw.getEndDate();
			break;
		}

		this.startTime = pw.getStart_time();
		this.endTime = pw.getEnd_time();
		this.allDay = pw.isAllDay();
	}


	@Override
	public String getEventSummary() {
		StringBuilder sb = new StringBuilder();
		if(frequency != 1){
			sb.append("Every ");
			sb.append(frequency.intValue());
			sb.append(" ");
		}
		switch(repeatType){
		case Daily:
			sb.append("Daily TO DO ");
			break;
		case Weekly:
			sb.append("Weekly on ");
			sb.append(getDaysOfWeek());
			sb.append(" ");
			break;
		case Monthly:
			sb.append("Montly TO DO ");
			break;
		case Yearly:
			sb.append("Yearly TO DO ");
			break;
		}
		sb.append("between");
		sb.append(getTimeRange());
		return sb.toString();
	}

	@Override
	public EventType getEventType() {
		return EventType.Recurring;
	}

	public String getDaysOfWeek(){
		List<Boolean> days = getRepeatsOnList();
		StringBuilder sb = new StringBuilder();
		if(days.get(0))
			sb.append("Sun ");
		if(days.get(1))
			sb.append("Mon ");
		if(days.get(2))
			sb.append("Tue ");
		if(days.get(3))
			sb.append("Wed ");
		if(days.get(4))
			sb.append("Thu ");
		if(days.get(5))
			sb.append("Fri ");
		if(days.get(6))
			sb.append("Sat ");
		return sb.toString();
	}

	public String getTimeRange() {
		if(allDay != null && allDay == true)
			return "all day";
		StringBuilder sb = new StringBuilder(DateTimeFormat.getFormat("H:mm").format(startTime));
		sb.append(" to ");
		sb.append(DateTimeFormat.getFormat("H:mm").format(endTime));
		return sb.toString();
	}

	public Long getDatabaseKey() {
		return databaseKey;
	}

	public void setDatabaseKey(Long databaseKey) {
		this.databaseKey = databaseKey;
	}

	public Long getLocationKey() {
		return locationKey;
	}

	public void setLocationKey(Long locationKey) {
		this.locationKey = locationKey;
	}

	public RepeatType getRepeatType() {
		return repeatType;
	}

	public Long getRepeatTypeAsLong() {
		switch(this.repeatType)
		{
		case Daily: return 0l;
		case Weekly: return 1l;
		case Monthly: return 2l;
		case Yearly: return 3l;
		}
		return 0l;
	}

	public void setRepeatType(RepeatType repeatType) {
		this.repeatType = repeatType;
	}

	public void setRepeatTypeViaLong(Long property) {
		switch(property.intValue())
		{
		case 0: 
			this.repeatType = RepeatType.Daily;
			break;
		case 1: 
			this.repeatType = RepeatType.Weekly;
			break;
		case 2: 
			this.repeatType = RepeatType.Monthly;
			break;
		case 3:
			this.repeatType = RepeatType.Yearly;
			break;
		}
	}

	public Long getFrequency() {
		return frequency;
	}

	public void setFrequency(Long frequency) {
		this.frequency = frequency;
	}

	public Boolean getAllDay() {
		return allDay;
	}

	public void setAllDay(Boolean allDay) {
		this.allDay = allDay;
	}

	public Date getStartDate() {
		return startDate;
	}

	public Date getStartTime() {
		return startTime;
	}

	public void setStartTime(Date startTime) {
		this.startTime = startTime;
	}

	public Date getEndTime() {
		return endTime;
	}

	public void setEndTime(Date endTime) {
		this.endTime = endTime;
	}

	public void setStartDate(Date startDate) {
		this.startDate = startDate;
	}

	public EndType getEndType() {
		return endType;
	}

	public Long getEndTypeAsLong(){
		switch(this.endType)
		{
		case Never: 
			return 0l;
		case AfterNumOccurrences:
			return 1l;
		case On:
			return 2l;
		}
		return 0l;
	}

	public void setEndType(EndType endType) {
		this.endType = endType;
	}

	public void setEndTypeViaLong(Long property) {
		switch(property.intValue())
		{
		case 0: 
			this.endType = EndType.Never;
			break;
		case 1:
			this.endType = EndType.AfterNumOccurrences;
			break;
		case 2:
			this.endType = EndType.On;
			break;
		}
	}

	public Long getNumberOfOccurences() {
		return numberOfOccurences;
	}

	public void setNumberOfOccurences(Long numberOfOccurences) {
		this.numberOfOccurences = numberOfOccurences;
	}

	public Date getEndDate() {
		return endDate;
	}

	public void setEndDate(Date endDate) {
		this.endDate = endDate;
	}

	public List<OneTimeEvent> getModifiedEventsInSeries() {
		return modifiedEventsInSeries;
	}

	public void setModifiedEventsInSeries(List<OneTimeEvent> modifiedEventsInSeries) {
		this.modifiedEventsInSeries = modifiedEventsInSeries;
	}

	public String getRepeatsOn() {
		return repeatsOn;
	}

	public List<Boolean> getRepeatsOnList(){
		List<Boolean> days = new LinkedList<Boolean>();
		for(int i = 0; i < repeatsOn.length(); ++i){
			if(repeatsOn.charAt(i) == '1')
				days.add(true);
			else
				days.add(false);
		}
		return days;
	}

	public void setRepeatsOn(String repeatsOn) {
		this.repeatsOn = repeatsOn;
	}

	public Boolean getDayOfMonth() {
		return dayOfMonth;
	}

	public void setDayOfMonth(Boolean dayOfMonth) {
		this.dayOfMonth = dayOfMonth;
	}
}
